
#include "HiBoost.Entity.h"

// [*] CHiBoostImageLayer

bool CHiBoostImageLayer::CPixRef::CompareBy_X_Y(const CPixRef& a, const CPixRef& b)
{
	if (a.layer_ix != b.layer_ix)
		return a.layer_ix < b.layer_ix;
	else
		return a.layer_iy < b.layer_iy;
}

const CHiBoostImageLayer::CPixRef* CHiBoostImageLayer::_MySearch_Y(const CPixRef* Hd, const CPixRef* Tl, int KeyY)
{
	int len = (int)(Tl - Hd);
	if (len < 1) return NULL;
	if (len == 1) return Hd;
	int ih, it, im;
	for ( ih = 0, it = len-1; ih < it; )
	{
		im = (ih + it) >> 1;
		if (Hd[im].layer_iy < KeyY)
			ih = im + 1;
		else
			it = im;
	}
	if (it + 1 < len) ++it;
	im = abs(Hd[ih].layer_iy - KeyY) - abs(Hd[it].layer_iy - KeyY);
	if (im <= 0)
		return Hd + ih;
	else
		return Hd + it;
}

int CHiBoostImageLayer::ComputePixelDistance(int dx, int dy)
{
	return abs(dx) + abs(dy);
}

CHiBoostImageLayer::CHiBoostImageLayer()
{
	Clear();
}

size_t CHiBoostImageLayer::GetMemoryUsage() const
{
	return sizeof(CImagePixelSampler_EdgeAndRandom*)
		+ sizeof(int) * 4
		+ sizeof(double) * 2
		+ CExternalTools::GetMemoryUsage(imgScaled)
		+ PixelList.GetMemoryUsage()
		+ PixRefList.GetMemoryUsage()
		+ X_StartList.GetMemoryUsage();
}

void CHiBoostImageLayer::Clear()
{
	Scaled_W = Scaled_H = 0;
	BaseImageLayerScale = 0.0;
	ZoomConfig.Clear();

	imgScaled.clear();

	PixelAmount = 0;
	PixelList.Clear();
	PixRefList.Clear();
	X_Amount = 0;
	X_StartList.Clear();
}

void CHiBoostImageLayer::SetImage(
	const CZoomableImage& ZoomImage,
	double xScale, double yScale,
	CImagePixelSampler_EdgeAndRandom* pIPS_ForInference,
	double GradientComputing_GaussianSigma,
	bool GradientComputing_fUseSignedOrientation)
{
	if (pIPS_ForInference == NULL) return;

	this->ZoomConfig.myInit(xScale, yScale);
	BaseImageLayerScale = sqrt(xScale * yScale);

	ZoomImage.BuildImage(imgScaled, xScale, yScale, CDenseImage::EXPANDING_MODE_EDGE);
	Scaled_W = imgScaled.width;
	Scaled_H = imgScaled.height;
	//tfmConfig.Clear();
	//tfmConfig.PushBack(new C_ImgTran_Scale(SourceImage.W * 0.5, SourceImage.H * 0.5, xScale, yScale));
	//SourceImage.NormalizeTransformedSize(Scaled_W, Scaled_H, tfmConfig);
	//SourceImage.BuildImage(imgScaled, Scaled_W, Scaled_H, tfmConfig, CDenseImage::EXPANDING_MODE_EDGE);

	CImageFilterBank* pIFB = new CImageFilterBank();
	{
		CImageFilter_RGB* pifRGB = pIFB->pBasicFilter_RGB;
		CImageFilter_Gradient* pifGradient = new CImageFilter_Gradient(GradientComputing_GaussianSigma, GradientComputing_fUseSignedOrientation);
		pifGradient->PushBackInputResponse(pifRGB, 0);
		pifGradient->PushBackInputResponse(pifRGB, 1);
		pifGradient->PushBackInputResponse(pifRGB, 2);
		pIFB->PushBackFilter(pifGradient);
		pifGradient->PushBackOutput(1);
		pIFB->GetOutputDimension(true);
	}
	pIFB->Work(imgScaled);

	pIPS_ForInference->Work(PixelList, imgScaled);
	PixelAmount = (int)PixelList.Size;

	//CImg<int> imgOutput;
	//imgOutput = imgScaled;
	//for ( int iy = 0; iy < Scaled_H; ++iy ) for ( int ix = 0; ix < Scaled_W; ++ix )
	//{
	//	imgOutput.at(ix, iy, 0, 0) = 128 + imgOutput.at(ix, iy, 0, 0) / 2;
	//	imgOutput.at(ix, iy, 0, 1) = 128 + imgOutput.at(ix, iy, 0, 1) / 2;
	//	imgOutput.at(ix, iy, 0, 2) = 128 + imgOutput.at(ix, iy, 0, 2) / 2;
	//}
	//for ( int ip = 0; ip < (int)PixelList.Size; ++ip )
	//{
	//	int ix = PixelList.pElement[ip] % Scaled_W;
	//	int iy = PixelList.pElement[ip] / Scaled_W;
	//	imgOutput.at(ix, iy, 0, 0) = 0;
	//	imgOutput.at(ix, iy, 0, 1) = 0;
	//	imgOutput.at(ix, iy, 0, 2) = 0;
	//}
	//char strTemp[10001];
	//sprintf_s(strTemp, "Pixel Amount = %d", PixelAmount);
	//CExternalTools::OutputTextToImage(imgOutput, strTemp, 0, 0, 255, 255, 255, 0, 0, 255);
	////imgOutput.display();
	//CExternalTools::mySaveImage("a.jpg", imgOutput);

	int i, lx;
	PixRefList.Resize(PixelAmount);
	CPixRef* ppr;
	const int* qip;
	const double* qgo = pIFB->GetOutput(0);
	matGradientOrientation.Clear();
	matGradientOrientation.PushBack(qgo, (int)(Scaled_W * Scaled_H));
	for ( ppr = PixRefList.pElement, qip = PixelList.pElement, i = 0; i < PixelAmount; ++i, ++qip, ++ppr )
	{
		ppr->index = i;
		ppr->layer_ix = *qip % Scaled_W;
		ppr->layer_iy = *qip / Scaled_W;
		ppr->layer_fx = ppr->layer_ix + 0.5;
		ppr->layer_fy = ppr->layer_iy + 0.5;
		
		ZoomConfig.Inverse(ppr->original_fx, ppr->original_fy, ppr->layer_fx, ppr->layer_fy);
		
		//ppr->gradient_orientation = qgo[*qip];
		//ppr->original_scale = BaseImageLayerScale;
	}
	sort(PixRefList.Begin(), PixRefList.End(), CPixRef::CompareBy_X_Y);
	for ( ppr = PixRefList.pElement, i = 0; i < PixelAmount; ++i, ++ppr )
		ppr->index = i;

	delete pIFB;

	X_Amount = 0;
	X_StartList.Clear();
	for ( lx = -1, ppr = PixRefList.pElement, i = 0; i < PixelAmount; ++i, ++ppr )
	{
		if (ppr->layer_ix <= lx) continue;
		++X_Amount;
		X_StartList.PushBack(i);
		lx = ppr->layer_ix;
	}
	X_StartList.PushBack(PixelAmount);
} 

void CHiBoostImageLayer::DrawPoints(CImg<int>& DstImage) const
{
	const int* _color = CExternalTools::GetIntArrayRGB(0, 0, 0);
	int i;
	for ( i = 0; i < (int)PixelList.Size; ++i )
		DstImage.draw_point(PixelList.pElement[i]%Scaled_W, PixelList.pElement[i]/Scaled_W, _color);

	char strTemp[100001];
	sprintf_s(strTemp, "|Pixels| = %d", PixelAmount);
	CExternalTools::OutputTextToImage(DstImage, strTemp, 0, 0, 255, 255, 0, 0, 0, 255);
}

void CHiBoostImageLayer::DrawPixSet(CImg<int>& DstImage) const
{
	DstImage = imgScaled;
	for ( int iy = 0; iy < Scaled_H; ++iy ) for ( int ix = 0; ix < Scaled_W; ++ix )
	{
		DstImage.at(ix, iy, 0, 0) = 128 + DstImage.at(ix, iy, 0, 0) / 2;
		DstImage.at(ix, iy, 0, 1) = 128 + DstImage.at(ix, iy, 0, 1) / 2;
		DstImage.at(ix, iy, 0, 2) = 128 + DstImage.at(ix, iy, 0, 2) / 2;
	}
	DrawPoints(DstImage);
}

void CHiBoostImageLayer::BuildPixRef_OriginalCoordinate(CPixRef& DstPixRef, double original_fx, double original_fy) const
{
	DstPixRef.original_fx = original_fx;
	DstPixRef.original_fy = original_fy;

	ZoomConfig.Work(DstPixRef.layer_fx, DstPixRef.layer_fy, original_fx, original_fy);

	DstPixRef.layer_ix = CMyMath::TranslateNumber_Floor(DstPixRef.layer_fx);
	DstPixRef.layer_iy = CMyMath::TranslateNumber_Floor(DstPixRef.layer_fy);
	//DstPixRef.gradient_orientation = GetGradientOrientation_IntLayerCoordinate(DstPixRef.layer_ix, DstPixRef.layer_iy);
	//DstPixRef.original_scale = BaseImageLayerScale;
}

void CHiBoostImageLayer::BuildPixRef_LayerCoordinate(CPixRef& DstPixRef, double layer_fx, double layer_fy) const
{
	ZoomConfig.Inverse(DstPixRef.original_fx, DstPixRef.original_fy, layer_fx, layer_fy);

	DstPixRef.layer_fx = layer_fx;
	DstPixRef.layer_fy = layer_fy;
	DstPixRef.layer_ix = CMyMath::TranslateNumber_Floor(DstPixRef.layer_fx);
	DstPixRef.layer_iy = CMyMath::TranslateNumber_Floor(DstPixRef.layer_fy);
	//DstPixRef.gradient_orientation = GetGradientOrientation_IntLayerCoordinate(DstPixRef.layer_ix, DstPixRef.layer_iy);
	//DstPixRef.original_scale = BaseImageLayerScale;
}

double CHiBoostImageLayer::GetGradientOrientation_IntLayerCoordinate(int layer_ix, int layer_iy) const
{
	layer_ix = (layer_ix<0 ? 0 : layer_ix>=Scaled_W ? Scaled_W-1 : layer_ix);
	layer_iy = (layer_iy<0 ? 0 : layer_iy>=Scaled_H ? Scaled_H-1 : layer_iy);
	return matGradientOrientation.pElement[layer_ix + layer_iy * Scaled_W];
}

const CHiBoostImageLayer::CPixRef* CHiBoostImageLayer::FindNearestSampledPixel_OriginalCoordinate(double original_fx, double original_fy) const
{
	double layer_fx, layer_fy;
	ZoomConfig.Work(layer_fx, layer_fy, original_fx, original_fy);
	return FindNearestSampledPixel_LayerCoordinate(layer_fx, layer_fy);
}

const CHiBoostImageLayer::CPixRef* CHiBoostImageLayer::FindNearestSampledPixel_LayerCoordinate(double layer_fx, double layer_fy) const
{
	const int* xsh = X_StartList.pElement;
	const CPixRef* prh = PixRefList.pElement;

	int ix = (int)layer_fx;
	int iy = (int)layer_fy;

	int ih, it, im;
	for ( ih = 0, it = X_Amount-1; ih < it; )
	{
		im = (ih + it) >> 1;
		if (prh[xsh[im]].layer_ix < ix)
			ih = im + 1;
		else
			it = im;
	}
	im = it;
	
	const CPixRef* ret;
	const CPixRef* qpr;
	int ans, t;
	ret = _MySearch_Y(prh + xsh[im], prh + xsh[im + 1], iy);
	ans = ComputePixelDistance(ret->layer_ix - ix, ret->layer_iy - iy);
	ih = im - 1;
	it = im + 1;
	bool ok = true;
	while (ok)
	{
		ok = false;
		if (ih >= 0)
		{
			t = CMyMath::sqr(prh[xsh[ih]].layer_ix - ix);
			if (t >= ans)
				ih = -1;
			else
			{
				ok = true;
				qpr = _MySearch_Y(prh + xsh[ih], prh + xsh[ih + 1], iy);
				t = ComputePixelDistance(qpr->layer_ix - ix, qpr->layer_iy - iy);
				if (t < ans)
				{
					ans = t;
					ret = qpr;
				}
				--ih;
			}
		}
		if (it < X_Amount)
		{
			t = CMyMath::sqr(prh[xsh[it]].layer_ix - ix);
			if (t >= ans)
				it = X_Amount;
			else
			{
				ok = true;
				qpr = _MySearch_Y(prh + xsh[it], prh + xsh[it + 1], iy);
				t = ComputePixelDistance(qpr->layer_ix - ix, qpr->layer_iy - iy);
				if (t < ans)
				{
					ans = t;
					ret = qpr;
				}
				++it;
			}
		}
	}

	return ret;
}

// [*] CHiBoostLayerRecord

void CHiBoostLayerRecord::myInit(
	const CZoomableImage& ZoomImage,
	//const CTransformableImage& tfmImage,
	int ScaleIndex, double ScaleFactor,
	CHiBoostNodeSetAppearanceIPFE& nsa,
	CImagePixelSampler_EdgeAndRandom* pPixelSampler,
	CSimpleTypeArray<CBinaryClassifier*>& FixedBcPtrList
	)
{
	int i, ix, iy, ti, si;
	int j, jx, jy, tj, sj;
	double tScore;
	char strTemp[10001];
	CImg<int> imgSrc;

	if (ScaleIndex > 0) ScaleIndex = 0;
	this->ScaleIndex = ScaleIndex;
	double real_scale = pow(ScaleFactor, ScaleIndex);
	if (real_scale > 1.0) real_scale = 1.0;

	ZoomConfig.myInit(real_scale);
	ZoomImage.BuildImage(imgSrc, real_scale, real_scale, CDenseImage::EXPANDING_MODE_EDGE);
	oW = imgSrc.width;
	oH = imgSrc.height;
	//tfmConfig.Clear();
	//tfmConfig.PushBack(new C_ImgTran_Scale(tfmImage.W * 0.5, tfmImage.H * 0.5, real_scale, real_scale));
	//tfmImage.NormalizeTransformedSize(this->oW, this->oH, tfmConfig);
	//tfmImage.BuildImage(imgSrc, oW, oH, tfmConfig, CDenseImage::EXPANDING_MODE_EDGE);

	oWH = oW * oH;

	CImagePositionFeatureExtractor* pIPFE = nsa.GetNodeAppearnceIPFE(-1)->myClone();
	pIPFE->SetImage(imgSrc, "");

	CSimpleTypeArray<int> SampledPixelList;
	pPixelSampler->Work(SampledPixelList, imgSrc);
	Amount_Record = (int)SampledPixelList.Size;

	CTimer timClfApp;
	timClfApp.Reset();
	timClfApp.Start();
	sprintf_s(strTemp, "app_level [%+3d].[%5d]...", ScaleIndex, Amount_Record);
	//CExternalTools::ConsoleTools::ClearCurrentLine();
	//cout<<strTemp;

	Length_Record = (int)FixedBcPtrList.Size;
	matRecordScore.Resize(Amount_Record * Length_Record);
	matRecordScore.Clear();
	for ( j = 0; j < Length_Record; ++j )
	{
		nsa.GetNodeAppearnceIPFE(j);
		pIPFE->RecomputeFeatureDimension();
		for ( i = 0; i < Amount_Record; ++i )
		{
			ti = SampledPixelList.pElement[i];
			ix = ti % oW; iy = ti / oW;
			pIPFE->SetQuery(ix, iy);
			FixedBcPtrList.pElement[j]->Classify(pIPFE, tScore, sj);
			matRecordScore.PushBack(tScore);
		}
	}

	CDataStructureTools::MyRelease_Single(pIPFE);

	matNearestRecord.Resize(oWH); matNearestRecord.Fill(-1);
	CSimpleTypeArray<int> qQueue;
	qQueue.Clear();
	int qh, qt;
	qt = 0;
	for ( i = 0; i < Amount_Record; ++i )
	{
		ti = SampledPixelList.pElement[i];
		matNearestRecord.pElement[ti] = i;
		qQueue.PushBack(ti);
		++qt;
	}
	for ( qh = 0; qh < qt; ++qh )
	{
		ti = qQueue.pElement[qh];
		ix = ti % oW; iy = ti / oW;
		si = matNearestRecord.pElement[ti];
		for ( j = 0; j < 4; ++j )
		{
			jx = ix + (j==0 ? -1 : (j==1 ? +1 : 0));
			jy = iy + (j==2 ? -1 : (j==3 ? +1 : 0));
			tj = jx + jy * oW;
			if (jx<0 || jx>=oW || jy<0 || jy>=oH || matNearestRecord.pElement[tj]>=0) continue;
			matNearestRecord.pElement[tj] = si;
			qQueue.PushBack(tj);
			++qt;
		}
	}

	timClfApp.Stop();
	sprintf_s(strTemp, " costed %s;", CExternalTools::GetTimeText(timClfApp.GetElapsedTime()));
	//cout<<strTemp<<endl;
}

double CHiBoostLayerRecord::GetAppearanceScore_OriginalCoordinate(double original_fx, double original_fy, int NodeIndex)
{
	if (NodeIndex<0 || NodeIndex>=Length_Record) return 0.0;
	double layer_fx, layer_fy;
	int layer_ix, layer_iy;

	ZoomConfig.Work(layer_fx, layer_fy, original_fx, original_fy);

	layer_ix = CMyMath::TranslateNumber_Floor(layer_fx);
	layer_iy = CMyMath::TranslateNumber_Floor(layer_fy);
	layer_ix = (layer_ix<0 ? 0 : (layer_ix>=oW ? oW-1 : layer_ix));
	layer_iy = (layer_iy<0 ? 0 : (layer_iy>=oH ? oH-1 : layer_iy));
	int RecordIndex = matNearestRecord.pElement[layer_ix + layer_iy * oW];
	return matRecordScore.pElement[(size_t)RecordIndex + ((size_t)NodeIndex) * (size_t)Amount_Record];
	//return matRecordScore.pElement[(size_t)NodeIndex + ((size_t)RecordIndex) * (size_t)Length_Record];
}

// [*] CHiBoostImageEntity::CAppearanceScoreRecord

bool CHiBoostImageEntity::CAppearanceScoreRecord::operator< (const CAppearanceScoreRecord& AnotherInstance) const
{
	if (NodeIndex != AnotherInstance.NodeIndex) return (NodeIndex < AnotherInstance.NodeIndex);
	if (QueryX != AnotherInstance.QueryX) return (QueryX < AnotherInstance.QueryX);
	if (QueryY != AnotherInstance.QueryY) return (QueryY < AnotherInstance.QueryY);
	return (Index_Scale < AnotherInstance.Index_Scale);
}

bool CHiBoostImageEntity::CAppearanceScoreRecord::operator== (const CAppearanceScoreRecord& AnotherInstance) const
{
	return (1
		&& (NodeIndex == AnotherInstance.NodeIndex)
		&& (QueryX == AnotherInstance.QueryX)
		&& (QueryY == AnotherInstance.QueryY)
		&& (Index_Scale == AnotherInstance.Index_Scale));
}

// [*] CHiBoostImageEntity

CHiBoostImageEntity::CConfig CHiBoostImageEntity::s_Config;

CHiBoostImageEntity::CHiBoostImageEntity()
{
	//_pConfig = NULL;
	pIPPFE = NULL;
	_Last_nsa_NodeIndex = -1;

	strFN_ImageCache = "";
	ClearAppearanceScoreCountLog_FullImage();
}

CHiBoostImageEntity::~CHiBoostImageEntity()
{
	SetFN_ImageCache("");
	_Last_nsa_NodeIndex = -1;
	CDataStructureTools::MyRelease_Single(pIPPFE);
	CDataStructureTools::ReleasePointers(List_PtrLayerRecord);
}

size_t CHiBoostImageEntity::GetMemoryUsage() const
{
	size_t ret = sizeof(CConfig*) + sizeof(CImagePyramidPositionFeatureExtractor*)
		+ CExternalTools::GetMemoryUsage(imgSource)
		+ ZoomImage.GetMemoryUsage()
		//+ tfmImage.GetMemoryUsage()
		+ (pIPPFE==NULL ? 0 : pIPPFE->GetMemoryUsage())
		+ (nsa.pIPFE==NULL ? 0 : nsa.pIPFE->GetMemoryUsage());
	return ret;
}

//void CHiBoostImageEntity::InitSettings(const CConfig* pFixedConfig)
//{
//	_pConfig = pFixedConfig;
//}

void CHiBoostImageEntity::SetImage(string strFN_SourceImage, string strImageHashName, bool fIsFlipped)
{
	CImg<int> imgTemp;
	CExternalTools::myLoadImage(strFN_SourceImage, imgTemp);
	SetImage(imgTemp, strImageHashName, fIsFlipped);
}

void CHiBoostImageEntity::SetImage(const CImg<int>& SourceImage, string strImageHashName, bool fIsFlipped)
{
	if (fIsFlipped)
	{
		imgSource.assign(SourceImage.width, SourceImage.height, 1, SourceImage.dim);
		int ix, iy, iv;
		for ( iv = 0; iv < (int)SourceImage.dim; ++iv) for ( iy = 0; iy < (int)SourceImage.height; ++iy ) for ( ix = 0; ix < (int)SourceImage.width; ++ix )
			imgSource.at(ix, iy, iv) = SourceImage.at(SourceImage.width - 1 - ix, iy, iv);
	}
	else
		imgSource = SourceImage;

	ZoomImage.myInit(imgSource);
	//tfmImage.myInit(imgSource);
	Original_W = imgSource.width;
	Original_H = imgSource.height;

	if (pIPPFE == NULL)
	{
		CImagePositionFeatureExtractor* pIPFE = nsa.GetNodeAppearnceIPFE(-1);
		pIPPFE = new CImagePyramidPositionFeatureExtractor(pIPFE->myClone(), s_Config.IPFE_CacheSize);
		_Last_nsa_NodeIndex = -1;
	}
	pIPPFE->SetImage(imgSource, "");

	ParsedLeafInstanceList.Clear();
	//List_PtrParsedLeafInstance.Clear();

	if (strImageHashName == "")
		SetFN_ImageCache("");
	else
		SetFN_ImageCache(strImageHashName + (fIsFlipped ? ".2.dat" : ".1.dat"));
	ClearAppearanceScoreCountLog_FullImage();

	Tag = 0;

	CurrentImageLayer.Clear();
	Amount_PixRef = 0;
	PtrPixRef = NULL;

	matGtNodeInst.DeepClear();
	matGtNodeInst.Resize(s_Config.pExampleSettings->NodeAmount);
	for ( int ii = 0; ii < (int)matGtNodeInst.Size; ++ii )
		matGtNodeInst.pElement[ii].Clear();

	timAppCalc.Reset();
}

void CHiBoostImageEntity::ReadParsedLeafInstances(const CHiBoostNodeInstanceBoxSet* pSrcParsedInstance)
{
	double ts = s_Config.pExampleSettings->ComputeGeneralIplScale(pSrcParsedInstance);
	//s_Config.pHiBoostExampleConfig->ComputeParsedObject_ImageLayerScale(ts, pSrcParsedInstance);
	SetCurrentImageLayer_ByScale(ts);

	ParsedLeafInstanceList.Clear();
	//List_PtrParsedLeafInstance.Clear();
	int i;
	const double* qx = pSrcParsedInstance->GetPtrLeaf_X();
	const double* qy = pSrcParsedInstance->GetPtrLeaf_Y();
	for ( i = 0; i < pSrcParsedInstance->GetLeafAmount(); ++i, ++qx, ++qy )
	{
		CHiBoostImageLayer::CPixRef tPixRef;
		CurrentImageLayer.BuildPixRef_OriginalCoordinate(tPixRef, *qx, *qy);
		tPixRef.index = -1;
		ParsedLeafInstanceList.PushBack(tPixRef);

		//List_PtrParsedLeafInstance.PushBack(FindNearestSampledPixel_OriginalCoordinate(*qx, *qy));
	}
}

//void CHiBoostImageEntity::ReadGroundTruth(string strFN_GroundTruth)
//{
//	CHiBoostGroundTruth tGT;
//
//	tGT.myInit(strFN_GroundTruth, s_Config.pHiBoostNodeInstanceBoxConfig);
//	this->ReadParsedLeafInstances(&tGT);
//}

bool CHiBoostImageEntity::SetCurrentImageLayer_ByScale(double Scale)
{
	//if (Original_W*Scale<s_Config.LayerMinWidth || Original_H*Scale<s_Config.LayerMinHeight)
	//	return false;

	CurrentImageLayer.SetImage(ZoomImage, Scale, Scale,
	//CurrentImageLayer.SetImage(tfmImage, Scale, Scale,
		s_Config.pIPS_ForInference, 
		s_Config.GradientComputing_GaussianSigma,
		s_Config.GradientComputing_fUseSignedOrientation);

	Amount_PixRef = CurrentImageLayer.PixelAmount;
	PtrPixRef = CurrentImageLayer.PixRefList.pElement;

	ClearAppearanceScoreCountLog_CurrentLayer();

	if (CurrentImageLayer.Scaled_W<s_Config.LayerMinWidth || CurrentImageLayer.Scaled_H<s_Config.LayerMinHeight)
		return false;
	return true;
}

bool CHiBoostImageEntity::SetCurrentImageLayer(int LayerIndex)
{
	double scale = 1.0 / pow(s_Config.ScaleFactor_PerceptronParser, LayerIndex);
	return SetCurrentImageLayer_ByScale(scale);
}

const CHiBoostImageLayer::CPixRef* CHiBoostImageEntity::FindNearestSampledPixel_OriginalCoordinate(double original_fx, double original_fy) const
{
	return CurrentImageLayer.FindNearestSampledPixel_OriginalCoordinate(original_fx, original_fy);
}

const CHiBoostImageLayer::CPixRef* CHiBoostImageEntity::FindNearestSampledPixel_LayerCoordinate(double layer_fx, double layer_fy) const
{
	return CurrentImageLayer.FindNearestSampledPixel_LayerCoordinate(layer_fx, layer_fy);
}

void CHiBoostImageEntity::ClearAppearanceScoreCountLog_CurrentLayer()
{
	//timAppearanceComputing_CurrentLayer.Reset();
	Amount_QueryReceived_CurrentLayer = 0;
	Amount_CalculationMade_CurrentLayer = 0;
}

void CHiBoostImageEntity::ClearAppearanceScoreCountLog_FullImage()
{
	ClearAppearanceScoreCountLog_CurrentLayer();

	//timAppearanceComputing_FullImage.Reset();
	Amount_QueryReceived_FullImage = 0;
	Amount_CalculationMade_FullImage = 0;

	//AppearanceScoreHistory.clear();

	CDataStructureTools::ReleasePointers(List_PtrLayerRecord);
}

void CHiBoostImageEntity::SetFN_ImageCache(string strFN_NewImageCache)
{
	CSimpleTypeArray<int> nxyiList;
	CSimpleTypeArray<double> sList;
	int amount_asr;

	if (this->strFN_ImageCache != "")
	{
		// Save ASH
		amount_asr = (int)AppearanceScoreHistory.size();
		if (amount_asr > 0)
		{
			nxyiList.Resize(amount_asr * 4); nxyiList.Clear();
			sList.Resize(amount_asr); sList.Clear();
			set<CAppearanceScoreRecord>::const_iterator itr;
			for ( itr = AppearanceScoreHistory.begin(); itr != AppearanceScoreHistory.end(); ++itr )
			{
				const CAppearanceScoreRecord& asr = *itr;
				nxyiList.PushBack(asr.NodeIndex);
				nxyiList.PushBack(asr.QueryX);
				nxyiList.PushBack(asr.QueryY);
				nxyiList.PushBack(asr.Index_Scale);
				sList.PushBack(asr.Score);
			}

			FILE* FOUT = fopen(this->strFN_ImageCache.c_str(), "wb");
			CDataStructureTools::my_fwrite_single(FOUT, amount_asr);
			CDataStructureTools::my_fwrite(FOUT, nxyiList);
			CDataStructureTools::my_fwrite(FOUT, sList);
			fclose(FOUT);
		}
	}

	this->strFN_ImageCache = strFN_NewImageCache;
	AppearanceScoreHistory.clear();

	if (this->strFN_ImageCache != "")
	{
		// Load ASH
		bool ok = true;
		FILE* FIN = fopen(this->strFN_ImageCache.c_str(), "rb");
		ok = ok && (CDataStructureTools::my_fread_single(FIN, amount_asr) == 1);
		if (ok && amount_asr > 0)
		{
			nxyiList.Resize(amount_asr * 4);
			sList.Resize(amount_asr);
			ok = ok && (CDataStructureTools::my_fread(FIN, nxyiList) == nxyiList.Size);
			ok = ok && (CDataStructureTools::my_fread(FIN, sList) == sList.Size);
			fclose(FIN);

			if (!ok) amount_asr = 0;
			for ( int i_asr = 0; i_asr < amount_asr; ++i_asr )
			{
				CAppearanceScoreRecord asr;
				asr.NodeIndex = nxyiList.pElement[i_asr * 4 + 0];
				asr.QueryX = nxyiList.pElement[i_asr * 4 + 1];
				asr.QueryY = nxyiList.pElement[i_asr * 4 + 2];
				asr.Index_Scale = nxyiList.pElement[i_asr * 4 + 3];
				asr.Score = sList.pElement[i_asr];
				AppearanceScoreHistory.insert(asr);
			}
		}
	}
}

double CHiBoostImageEntity::GetAppearanceScore_LayerCoordinate(double layer_fx, double layer_fy, double inference_scale, int NodeIndex)
{
	//return 0.0;

	if (NodeIndex<0 || NodeIndex>=(int)s_Config.FixedBcPtrList.Size) return 0.0;

	++Amount_QueryReceived_CurrentLayer;
	++Amount_QueryReceived_FullImage;

	double ideal_layer_scale = CurrentImageLayer.BaseImageLayerScale / inference_scale;
	if (ideal_layer_scale > 1.0) ideal_layer_scale = 1.0;
	int ScaleIndex = CMyMath::TranslateNumber_Round(log(ideal_layer_scale) / log(s_Config.ScaleFactor_AppearanceModel), (int)0);
	if (ScaleIndex > 0) ScaleIndex = 0;
	double real_layer_scale = pow(s_Config.ScaleFactor_AppearanceModel, ScaleIndex);
	if (real_layer_scale > 1.0) real_layer_scale = 1.0;

	CHiBoostImageLayer::CPixRef tpr;
	if (s_fFindNearestSampledPixel)
		tpr = *CurrentImageLayer.FindNearestSampledPixel_LayerCoordinate(layer_fx, layer_fy);
	else
		CurrentImageLayer.BuildPixRef_LayerCoordinate(tpr, layer_fx, layer_fy);

	CAppearanceScoreRecord asr;
	asr.NodeIndex = NodeIndex;
	asr.QueryX = CMyMath::TranslateNumber_Floor(tpr.original_fx);
	asr.QueryY = CMyMath::TranslateNumber_Floor(tpr.original_fy);
	asr.Index_Scale = ScaleIndex;
	asr.Score = 0.0;

	bool ok = false;

	//if (s_fUseAppearanceScoreCache && asr.QueryX!=248 && asr.QueryY!=360)
	if (s_fUseAppearanceScoreCache)
	{
		set<CAppearanceScoreRecord>::iterator itr;
		itr = AppearanceScoreHistory.find(asr);

		if (itr != AppearanceScoreHistory.end())
		{
			asr = *itr;
			ok = true;
		}
	}
	if (!ok)
	{
		//timAppearanceComputing_CurrentLayer.Start();
		//timAppearanceComputing_FullImage.Start();
		++Amount_CalculationMade_CurrentLayer;
		++Amount_CalculationMade_FullImage;
		double tx, ty;

		CRectBox<double> dbox;
		s_Config.pExampleSettings->GetNodeBicExampleConfig(NodeIndex).MakeContextBoxFromCenterAndIplScale(dbox, tpr.original_fx, tpr.original_fy, real_layer_scale);
		//s_Config.pHiBoostExampleConfig->pHiBoostNodeContextConfig->GetContextBoxForNodeInstance(dbox, tpr.original_fx, tpr.original_fy, 1.0 / real_layer_scale, NodeIndex);
		tx = dbox.x + dbox.w * 0.5;
		ty = dbox.y + dbox.h * 0.5;
		////s_Config.pHiBoostExampleConfig->pHiBoostNodeContextConfig->GetStdboxForNodeInstance(tx, ty, tpr.original_fx, tpr.original_fy, NodeIndex, real_layer_scale, real_layer_scale);

		if (s_fUseGlobalRecordCache)
		{
			int i;
			for ( i = 0; i < (int)List_PtrLayerRecord.Size; ++i )
				if (List_PtrLayerRecord.pElement[i]->ScaleIndex == ScaleIndex)
					break;
			if (i >= (int)List_PtrLayerRecord.Size)
			{
				CHiBoostLayerRecord* NewLayerRecord = new CHiBoostLayerRecord();
				NewLayerRecord->myInit(ZoomImage, ScaleIndex, s_Config.ScaleFactor_AppearanceModel, nsa, s_Config.pIPS_ForAppearance, s_Config.FixedBcPtrList);
				List_PtrLayerRecord.PushBack(NewLayerRecord);
				i = (int)List_PtrLayerRecord.Size - 1;
			}
			asr.Score = List_PtrLayerRecord.pElement[i]->GetAppearanceScore_OriginalCoordinate(tpr.original_fx, tpr.original_fy, NodeIndex);
		}
		else
		{
			timAppCalc.Start();

			RefreshIPPFE(NodeIndex);
			//nsa.GetNodeAppearnceIPFE(NodeIndex);
			//pIPPFE->RecomputeFeatureDimension();

			CImagePyramidPosition ipp;
			ipp.InitNew(tx, ty, real_layer_scale, real_layer_scale);
			pIPPFE->SetQuery(ipp);
			int tPrediciton = 0;
			asr.Score = s_Config.FixedBcPtrList.pElement[NodeIndex]->Classify(pIPPFE);

			timAppCalc.Stop();
		}

		//timAppearanceComputing_CurrentLayer.Stop();
		//timAppearanceComputing_FullImage.Stop();
		if (s_fUseAppearanceScoreCache)
			AppearanceScoreHistory.insert(asr);
	}

	return asr.Score;
}

void CHiBoostImageEntity::RefreshIPPFE(int NodeIndex)
{
	if (_Last_nsa_NodeIndex==NodeIndex && nsa.Last_nsa_NodeIndex==NodeIndex) return;
	if (nsa.Last_nsa_NodeIndex != NodeIndex)
		nsa.GetNodeAppearnceIPFE(NodeIndex);
	_Last_nsa_NodeIndex = NodeIndex;
	pIPPFE->RecomputeFeatureDimension();
}

